package it.antonio.ilpizze.te;

import java.util.Stack;

import it.antonio.ilpizze.te.token.CommandRenderCommand;
import it.antonio.ilpizze.te.token.InvertedCommandRenderCommand;
import it.antonio.ilpizze.te.token.NodeRenderCommand;
import it.antonio.ilpizze.te.token.RootRenderCommand;
import it.antonio.ilpizze.te.token.TextRenderCommand;
import it.antonio.ilpizze.te.token.ValueRenderCommand;

public class Parser {

	public static RenderCommand parse(Tokens tokens){
		
		NodeRenderCommand node = new RootRenderCommand();
		
		Token markToken = tokens.first();
		
		Stack<String> operationStack = new Stack<String>();
		
		while(markToken != null){
			
			Token currentToken = markToken;
			
			// QUESTA VA RIVISTA: ADESSO NON HA SENSO
			if(currentToken.isSpecial(Token.ESCAPE)){
				if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
				currentToken = currentToken.next;
				
				node.add(new TextRenderCommand(currentToken.asText()));
				markToken = currentToken.next;
				continue;
			}
			
			if(currentToken.isSpecial(Token.OPEN)){
				if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
				
				currentToken = currentToken.next;
				
				// VALUE
				if(currentToken.isSpecial(Token.VALUE)){
					
					if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
					
					currentToken = currentToken.next;
					if(currentToken.isText()){
						String varName = currentToken.asText();
						String property = null;
						String filter = null;
						
						if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
						
						currentToken = currentToken.next;
						if(currentToken.isSpecial(Token.POINT)){
							if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
							
							currentToken = currentToken.next;
							if(currentToken.isText()){
								property = currentToken.asText();
								currentToken = currentToken.next;
							} else {
								throw new ParseException("expected property name", currentToken.position);
							}
							
						}
						
						if(currentToken.isSpecial(Token.FILTER)){
							if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
							
							currentToken = currentToken.next;
							if(currentToken.isText()){
								filter = currentToken.asText();
								currentToken = currentToken.next;
							} else {
								throw new ParseException("expected property name", currentToken.position);
							}
							
						}
						
						if(currentToken.isSpecial(Token.CLOSE)){
							node.add(new ValueRenderCommand(varName, property, filter));
						} else {
							throw new ParseException("expected close bracket", currentToken.position);
						}
						
					} else {
						throw new ParseException("expected value name", currentToken.position);
					}
					markToken = currentToken.next;
					continue;
				}
				
				
				// OPERATION
				if(currentToken.isSpecial(Token.OPERATION)){
					if(currentToken.hasNext() == false) throw new ParseException("next token expected",  currentToken.position);
					
					String varName = null;
					String property = null;
					String as = null;
					String filter = null;
					currentToken = currentToken.next;
					if(currentToken.isText()){
						varName = currentToken.asText();
						
						currentToken = currentToken.next;
						if(currentToken.isSpecial(Token.POINT)){
							if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
							
							currentToken = currentToken.next;
							if(currentToken.isText()){
								property = currentToken.asText();
								currentToken = currentToken.next;
								
								operationStack.push(varName + Token.POINT + property);	
							} else {
								throw new ParseException("expected property name", currentToken.position);
							}
							
						} else {
							operationStack.push(varName);	
						}
						
					} else {
						throw new ParseException("expected operation name",  currentToken.position);
					}
					
					if(currentToken.isSpecial(Token.FILTER)){
						if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
						
						currentToken = currentToken.next;
						if(currentToken.isText()){
							filter = currentToken.asText();
							currentToken = currentToken.next;
						} else {
							throw new ParseException("expected property name", currentToken.position);
						}
						
					}
					
					if(currentToken.isSpecial(Token.TWO_POINTS)){
						if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
						
						currentToken = currentToken.next;
						if(currentToken.isText()){
							as = currentToken.asText();
							currentToken = currentToken.next;
						} else {
							throw new ParseException("expected AS",currentToken.position);
						}
						
					}

					if(currentToken.isSpecial(Token.CLOSE)){
						NodeRenderCommand newNode = new CommandRenderCommand(node, varName,  property, filter, as);
						node.add(newNode);
						node = newNode;
						
					} else {
						throw new ParseException("expected close bracket", currentToken.position);
					}
					
					markToken = currentToken.next;
					continue;
				}	
				
				// INVERTED_OPERATION
				if(currentToken.isSpecial(Token.INVERTED_OPERATION)){
					if(currentToken.hasNext() == false) throw new ParseException("next token expected",  currentToken.position);
					
					String varName = null;
					String property = null;
					String filter = null;
					currentToken = currentToken.next;
					if(currentToken.isText()){
						varName = currentToken.asText();
						
						currentToken = currentToken.next;
						if(currentToken.isSpecial(Token.POINT)){
							if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
							
							currentToken = currentToken.next;
							if(currentToken.isText()){
								property = currentToken.asText();
								currentToken = currentToken.next;
								
								operationStack.push(varName + Token.POINT + property);	
							} else {
								throw new ParseException("expected property name", currentToken.position);
							}
							
						} else {
							operationStack.push(varName);	
						}
						
					} else {
						throw new ParseException("expected operation name",  currentToken.position);
					}
					
					if(currentToken.isSpecial(Token.FILTER)){
						if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
						
						currentToken = currentToken.next;
						if(currentToken.isText()){
							filter = currentToken.asText();
							currentToken = currentToken.next;
						} else {
							throw new ParseException("expected property name", currentToken.position);
						}
						
					}
					
					if(currentToken.isSpecial(Token.TWO_POINTS)){
						throw new ParseException("AS not supported for inverted operator", currentToken.position);
					}

					if(currentToken.isSpecial(Token.CLOSE)){
						NodeRenderCommand newNode = new InvertedCommandRenderCommand(node, varName,  property, filter);
						node.add(newNode);
						node = newNode;
						
					} else {
						throw new ParseException("expected close bracket", currentToken.position);
					}
					
					markToken = currentToken.next;
					continue;
				}
				
				
				// END OPERATION
				if(currentToken.isSpecial(Token.END_OPERATION)){
					if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
					
					String varName = null;
					String property = null;
					currentToken = currentToken.next;
					if(currentToken.isText()){
						
						String varOnStack = operationStack.pop();
						
						
						varName = currentToken.asText();
						currentToken = currentToken.next;
						
						if(currentToken.isSpecial(Token.POINT)){
							if(currentToken.hasNext() == false) throw new ParseException("next token expected", currentToken.position);
							
							currentToken = currentToken.next;
							if(currentToken.isText()){
								property = currentToken.asText();
								currentToken = currentToken.next;
								
								if(( varName + Token.POINT + property).equals(varOnStack) == false){
									throw new ParseException("expected close operation: " + varOnStack + "; found: " + varName + Token.POINT + property, currentToken.position);
								}
							} else {
								throw new ParseException("expected property name", currentToken.position);
							}
							
						} else {
							if(varName.equals(varOnStack) == false){
								throw new ParseException("expected close operation: " + varOnStack + "; found: " + varName, currentToken.position);
							}
						}
						
						
						
						
					} else {
						throw new ParseException("expected operation name",  currentToken.position);
					}
					
					if(currentToken.isSpecial(Token.CLOSE)){
						node = node.parent();
					} else {
						throw new ParseException("expected close bracket", currentToken.position);
					}
					
					markToken = currentToken.next;
					continue;
				}
			}
			
			
			node.add(new TextRenderCommand(markToken.asText()));
			markToken = markToken.next;
			continue;
		}
		
		return node;
	}

}
